{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 介绍\n",
    "\n",
    "千帆大模型平台现已支持用户使用 ERNIE-Bot 模型的 Function Call 功能。Function Call 功能允许用户向大模型传递其可以使用的工具集合，并且通过使用该集合中的工具来解决问题。\n",
    "\n",
    "千帆 Python SDK 在 Function Call 的基础上，与 Langchain 的 Agent 理念进行结合，推出了以 Function Call 功能实现的千帆 Agent 模块。下面将通过一个简单的搜索任务 Demo，来演示该模块的使用效果。\n",
    "\n",
    "# 准备工作\n",
    "\n",
    "若需要执行该 Demo，用户首先需要保证目前使用的 Python 版本高于 **3.8**，Langchain 版本高于 0.1.10，且安装最新版本的千帆 Python SDK 、 Wikipedia 与 arxiv 包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -U qianfan\n",
    "!pip install -U wikipedia\n",
    "!pip install -U arxiv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后根据[文档](https://cloud.baidu.com/doc/WENXINWORKSHOP/s/3lmokh7n6)指引申请API Key（即AK）和 Secret Key（即SK），这些都会在后续的流程中使用到。\n",
    "\n",
    "# 设置\n",
    "\n",
    "首先设置好你所申请到的千帆 AK 与 SK"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "os.environ['QIANFAN_AK'] = \"your_ak\"\n",
    "os.environ['QIANFAN_SK'] = \"your_sk\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其次创建对应 ERNIE-Bot 或者 ERNIE-Bot-4 模型的 Langchain Chat Model。model 字段支持使用`ERNIE-Bot`。 `ERNIE-Bot-4` 也会在将来支持function_call功能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models import QianfanChatEndpoint\n",
    "\n",
    "qianfan_chat_model = QianfanChatEndpoint(endpoint=\"ernie-3.5-8k-0205\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这之后，我们还需要创建传递给大模型的工具集。这里我们使用 Langchain 内自带的 Wikipedia 搜索 和 Arxiv 搜索工具。为了能够保证搜索结果在中文方面的相关性，此处我们设置了一些参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.tools import WikipediaQueryRun, ArxivQueryRun\n",
    "from langchain.utilities import ArxivAPIWrapper, WikipediaAPIWrapper\n",
    "\n",
    "tools = [\n",
    "    ArxivQueryRun(\n",
    "        description=\"这是一个论文搜索的工具\"\n",
    "        \"当你需要查询论文的时候，请使用这个工具\"\n",
    "        \"输入应该是一条你所需要查询的论文的信息\"\n",
    "        ,\n",
    "        api_wrapper=ArxivAPIWrapper(top_k_results=1)\n",
    "    ),\n",
    "    WikipediaQueryRun(\n",
    "        description=\"这是一个维基百科的调用工具.\"\n",
    "        \"当你需要查询有关论文作者、历史的百科信息时，请使用这个工具\"\n",
    "        \"输入应该是一条你所需要查询的内容\",\n",
    "        api_wrapper=WikipediaAPIWrapper(load_all_available_meta=True)\n",
    "    )\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果你想编写属于自己的工具，可以参考这篇 [Langchain 的官方文档](https://python.langchain.com/docs/modules/agents/tools/custom_tools#using-the-tool-decorator)\n",
    "\n",
    "在准备好上述组件后，我们就需要引入 Function Call 相关的千帆 Agent 组件了。\n",
    "\n",
    "# 千帆 Agent\n",
    "\n",
    "千帆 Agent 共分为两种：`QianfanSingleActionAgent` 和 `QianfanMultiActionAgent` ，两者的区别在于并行度的不同，前者为串行执行，一次规划只能使用一个工具；后者为并行执行，一次规划可以同时使用多个工具，但是规划的结果更加不稳定。在实际使用时，我们推荐使用 `QianfanSingleActionAgent` ，其能够在绝大多数场合下契合你的需求。本 Demo 使用 `QianfanSingleActionAgent` 进行演示。\n",
    "\n",
    "用户只需要如下图所示，调用工厂方法 `from_system_prompt` 即可创建一个千帆 Agent，并且作为 Langchain AgentExcutor 的参数传入。`from_system_prompt` 除了接受 `tools` 工具列表和 `llm` 以外，还支持用户传入名为 `system_prompt` 的可选 prompt，作为最终上报的 system 字段内容。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents import AgentExecutor\n",
    "\n",
    "from qianfan.extensions.langchain.agents import QianfanSingleActionAgent\n",
    "\n",
    "qianfan_agent = QianfanSingleActionAgent.from_system_prompt(tools, qianfan_chat_model)\n",
    "excutor = AgentExecutor(agent=qianfan_agent, tools=tools, verbose=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后我们就可以尝试去提问了。比如说，让 Agent 来帮我随便查询一篇有关 physics 的论文，并且返回作者的百科信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[WARNING] [03-28 18:18:54] base.py:526 [t:8094817088]: This key `functions` does not seem to be a parameter that the model `ERNIE-Bot-turbo` will accept\n",
      "[INFO] [03-28 18:18:54] openapi_requestor.py:336 [t:8094817088]: requesting llm api endpoint: /chat/ernie-3.5-8k-0205\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32;1m\u001b[1;3mcontent='' additional_kwargs={'finish_reason': 'function_call', 'request_id': 'as-xscack4vqj', 'object': 'chat.completion', 'search_info': []} response_metadata={'token_usage': {'prompt_tokens': 171, 'completion_tokens': 81, 'total_tokens': 252}, 'model_name': 'ERNIE-Bot-turbo', 'finish_reason': 'function_call', 'id': 'as-xscack4vqj', 'object': 'chat.completion', 'created': 1711621137, 'result': '', 'is_truncated': False, 'need_clear_history': False, 'function_call': {'name': 'arxiv', 'thoughts': '用户需要查询physics领域的论文以及作者的百科信息。这需要我调用arxiv工具和wikipedia工具。任务拆解：[sub-task1: 调用[arxiv]工具查询physics领域的论文, sub-task2: 调用[wikipedia]工具查询论文作者的百科信息]。接下来我需要调用[arxiv]工具来查询physics领域的论文。', 'arguments': '{\"query\":\"physics\"}'}, 'usage': {'prompt_tokens': 171, 'completion_tokens': 81, 'total_tokens': 252}}\u001b[0m"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[WARNING] [03-28 18:19:00] base.py:526 [t:8094817088]: This key `functions` does not seem to be a parameter that the model `ERNIE-Bot-turbo` will accept\n",
      "[INFO] [03-28 18:19:00] openapi_requestor.py:336 [t:8094817088]: requesting llm api endpoint: /chat/ernie-3.5-8k-0205\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[36;1m\u001b[1;3mPublished: 2012-09-04\n",
      "Title: Is Physics Sick? [In Praise of Classical Physics]\n",
      "Authors: Hisham Ghassib\n",
      "Summary: In this paper, it is argued that theoretical physics is more akin to an\n",
      "organism than to a rigid structure.It is in this sense that the epithet,\n",
      "\"sick\", applies to it. It is argued that classical physics is a model of a\n",
      "healthy science, and the degree of sickness of modern physics is measured\n",
      "accordingly. The malady is located in the relationship between mathematics and\n",
      "physical meaning in physical theory.\u001b[0m\u001b[32;1m\u001b[1;3mcontent='根据您的请求，我找到了一篇关于physics领域的论文。这篇论文的标题是“Is Physics Sick? [In Praise of Classical Physics]”，作者为Hisham Ghassib，发布于2012年9月4日。论文摘要中提到，理论物理更像一个有机体而非一个刚性的结构，现代物理在某种程度上存在病症，而经典物理则是一个健康的科学模型。\\n\\n关于作者的百科信息，由于Arxiv主要提供论文的搜索和查询功能，并不直接提供作者的百科信息。您可以通过搜索引擎或专门的百科网站来查找关于Hisham Ghassib的更多信息。如果您需要进一步的帮助或有其他问题，请随时告诉我。' additional_kwargs={'finish_reason': 'normal', 'request_id': 'as-3ct32tn9zg', 'object': 'chat.completion', 'search_info': []} response_metadata={'token_usage': {'prompt_tokens': 315, 'completion_tokens': 148, 'total_tokens': 463}, 'model_name': 'ERNIE-Bot-turbo', 'finish_reason': 'normal', 'id': 'as-3ct32tn9zg', 'object': 'chat.completion', 'created': 1711621149, 'result': '根据您的请求，我找到了一篇关于physics领域的论文。这篇论文的标题是“Is Physics Sick? [In Praise of Classical Physics]”，作者为Hisham Ghassib，发布于2012年9月4日。论文摘要中提到，理论物理更像一个有机体而非一个刚性的结构，现代物理在某种程度上存在病症，而经典物理则是一个健康的科学模型。\\n\\n关于作者的百科信息，由于Arxiv主要提供论文的搜索和查询功能，并不直接提供作者的百科信息。您可以通过搜索引擎或专门的百科网站来查找关于Hisham Ghassib的更多信息。如果您需要进一步的帮助或有其他问题，请随时告诉我。', 'is_truncated': False, 'need_clear_history': False, 'usage': {'prompt_tokens': 315, 'completion_tokens': 148, 'total_tokens': 463}}\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '帮我查询physics领域论文，以及作者的百科信息',\n",
       " 'output': '根据您的请求，我找到了一篇关于physics领域的论文。这篇论文的标题是“Is Physics Sick? [In Praise of Classical Physics]”，作者为Hisham Ghassib，发布于2012年9月4日。论文摘要中提到，理论物理更像一个有机体而非一个刚性的结构，现代物理在某种程度上存在病症，而经典物理则是一个健康的科学模型。\\n\\n关于作者的百科信息，由于Arxiv主要提供论文的搜索和查询功能，并不直接提供作者的百科信息。您可以通过搜索引擎或专门的百科网站来查找关于Hisham Ghassib的更多信息。如果您需要进一步的帮助或有其他问题，请随时告诉我。'}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "excutor.invoke(\"帮我查询physics领域论文，以及作者的百科信息\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 结语\n",
    "\n",
    "受限于可以免费使用的工具数量少，质量参差不齐；以及现有的大模型能力限制，目前 Agent 的体验还远远算不上优秀。\n",
    "\n",
    "如果读者在阅读该 demo 时遇到了什么问题或想提出意见，欢迎在项目中留下你的 issue。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  },
  "vscode": {
   "interpreter": {
    "hash": "58f7cb64c3a06383b7f18d2a11305edccbad427293a2b4afa7abe8bfc810d4bb"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
